package server_commet;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport;
import dropbox.common.Command;
import files_handler.UpdateInfo;
import serverDB_classes.DBManager;
import sun.misc.BASE64Decoder;

/**
 * servlet that link users to changes, 
 * it uses "commet" to notify and "push" 
 * new updates to user, this class observes 
 * the data base and send linked users the 
 * relevant update according to changes in the
 * data base of the server
 * 
 * @author Yoav
 *
 */
public class LinkClients extends HttpServlet implements Observer {

	private static final long serialVersionUID = 1L;

	/*
	 *	 
	 * holds the continuation object of a user
	 *
	 */
	private class CcHolder {
		private Continuation cc = null;		


		/*
		 * constructor 
		 * @param cc - user's cc
		 */
		public CcHolder(Continuation cc) {
			this.cc = cc;
		}
		
		/*
		 * get user's continuation object
		 * @return user's continuation object
		 */
		public Continuation getCc() {
			return cc;
		}
	}

	//holds all users and their CcHolder clasee
	private HashMap<String, CcHolder> hashMap = null;

	/**
	 * constructor
	 */
	public LinkClients() {
		//sign up to observe the data base
		DBManager.getDBManager().addObserver(this);

		hashMap = new HashMap<String, CcHolder>();
	}

	/**
	 * implements servlet service method
	 */
	public void service(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		//get continuation object
		Continuation cc = ContinuationSupport.getContinuation(req);
		
		final CcHolder cClass;		 
			
			if (req.getRequestURI().contains("/link")) {
				try{
					//if first time connected
					if (cc.isInitial()){
						//authenticate user
						String userName = authenticate(req);						
						if (userName != null){
							//create a new class for user
							cClass = new CcHolder(cc);
							synchronized (hashMap) {
								//map user
								hashMap.put(userName, cClass);
							}
							//suspend connection
							cClass.getCc().setTimeout(0);
							cClass.getCc().suspend();
						}
					}else{
						//not first time, user has already signed for updates
						//suspend connection after done
						cc.suspend();						
						ObjectOutputStream oos = null;
						try{

						    oos = new ObjectOutputStream(resp.getOutputStream());
							Command command = null;
							//get command
							command = (Command)req.getAttribute("command");

								if (command != null) {
									//send user the Command object		
									oos.writeObject(command);
									oos.flush();
								}
						}finally{
							//close out stream
							oos.close();
						}
					}
				}catch (IOException e){
				}
			}		
	}
	/**
	 * This method returns userName if the HttpServletRequest contains a valid
	 * Authorization header
	 * 
	 * @param req The HttpServletRequest
	 * @return user's name if the Authorization header is valid, null OW
	 **/

	private String authenticate(HttpServletRequest req) throws IOException {

		//get authorization header
		String authHead = req.getHeader("Authorization");

		if (authHead != null) {
			// *****Decode the authorization String*****
			BASE64Decoder decoder = new BASE64Decoder();
			//holds decoded user name and password
			String userNpass = null;
			try {
				userNpass = new String(decoder.decodeBuffer(authHead
						.substring(6)));
			} catch (IOException e) {
			}
			
			// *****Split the userName from the password*****
			String userName = userNpass.substring(0,userNpass.indexOf(":"));
			String password = userNpass.substring(userNpass.indexOf(":") + 1);

			//verify user in data base
			if (DBManager.getDBManager().verifyUser(userName, password)) {
				//Verified
				return userName;
			}
		}
		return null;
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		UpdateInfo up = (UpdateInfo) arg1;
		CcHolder ci = hashMap.get("yoavip@hotmail.com");
		if (ci != null) {
			ci.getCc().setAttribute("command",up.getChange());			
			ci.getCc().resume();
			/*
			 * for (String uname : up.getUsers() ){ ClientInfo ci =
			 * hashMap.get(uname); ci.enQueue(up.getChange());
			 * ci.getCc().resume(); }
			 */
		}

	}
}
